"""
Copyright (c) Facebook, Inc. and its affiliates.

This file generates a craft_recipes.h file that is used by the C++ client.
"""

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("items_ini")
parser.add_argument("crafting_txt")
parser.add_argument(
    "-o", "--out-file", required=True, help="path to craft_recipes.h file to write"
)
args = parser.parse_args()


# read items.ini

name2idm = {}

with open(args.items_ini, "r") as f:
    lines = f.readlines()

for line in reversed(lines):  # reverse so lower ids are used for duplicate names
    try:
        name, val = line.split("=")
    except ValueError:
        continue

    idm = tuple(map(int, val.split(":"))) if ":" in val else (int(val), 0)
    name2idm[name] = idm


# read crafting.txt


def fill_recipe(recipe, idm, xs, ys):
    for x in xs:
        for y in ys:
            idx = (y - 1) * 3 + (x - 1)
            if recipe[idx] is None:
                recipe[idx] = (idm, 1)
                return
            old_idm, count = recipe[idx]
            if old_idm == idm:
                recipe[idx] = (idm, count + 1)
                return
    raise Exception("Failed", recipe, idm, xs, ys)


idm2recipe = {}  # idm -> (recipe, count)

with open(args.crafting_txt, "r") as f:
    lines = f.readlines()

for line in lines:
    # strip comments, empty lines
    line = line.split("#")[0]
    if line.strip() == "":
        continue
    assert "=" in line

    result, ingredients = line.split("=")

    # parse result, count
    if "," in result:
        # has count
        result_name, count = tuple(map(str.strip, result.split(",")))
        count = int(count)
    else:
        # has no count (default=1)
        result_name = result.strip()
        count = 1

    try:
        result_idm = name2idm[result_name.lower()]
    except KeyError:
        print("Ignoring:", line.strip())
        continue

    # parse ingredients, fill recipe array

    recipe = [None] * 9  # array of (idm, count)

    ingredients = tuple(map(str.strip, ingredients.split("|")))
    for ingredient in ingredients:
        # get ingredient idm
        name, *locs = ingredient.replace(" ", "").split(",")
        name = name.split("^-1")[0]
        idm = name2idm[name.lower()]

        # get crafting table locations
        try:
            for loc in locs:
                if loc == "*":
                    fill_recipe(recipe, idm, (1, 2, 3), (1, 2, 3))
                    continue
                x, y = loc.split(":")
                if x == "*":
                    fill_recipe(recipe, idm, (1, 2, 3), (int(y),))
                elif y == "*":
                    fill_recipe(recipe, idm, (int(x),), (1, 2, 3))
                else:
                    fill_recipe(recipe, idm, (int(x),), (int(y),))
        except:
            print("Failed ing", ingredient.strip())

    idm2recipe[result_idm] = (recipe, count)


# print header file


def format_recipe(idm, recipe, count):
    key = (idm[0] << 8) | idm[1]
    recipe = [((0, 0), 0) if r is None else r for r in recipe]
    ingredients = ",".join(["{{{},{},{}}}".format(d, m, c) for ((d, m), c) in recipe])
    val = "{{{{{{{}}}}},{}}}".format(ingredients, count)
    return "{{{},{}}}".format(key, val)


HEADER = """
// Generated by python/craft_recipes.py

#pragma once
#include <array>
#include <unordered_map>

struct Ingredient {
  uint16_t id;
  uint8_t meta;
  uint8_t count;
};

struct Recipe {
  std::array<Ingredient, 9> ingredients;
  uint8_t count;
};

// Map key = (id << 8) | meta
const std::unordered_map<uint32_t, Recipe> RECIPES{
"""

HEADER += ",".join([format_recipe(idm, *recipe) for (idm, recipe) in idm2recipe.items()])
HEADER += "};\n"
with open(args.out_file, "w") as f:
    f.write(HEADER)
